#include "ThreeAddressCode.h"

std::string ThreeAddressCode::_opCodeToString[TAC_SENTRY] = { "error", "goto", "if_false", 
	"assign", "assign_to_adrress_saved_at", "address_of", "not", 
	"*", "/", "%",
	"+", "-", 
	"<", ">", "==", "<=", ">=", "!=",
	"&&", "||", 
	"call", "halt", "param", "numerical_constant", "character_constant", "no_op",
	"read_into", "write_contents_of", "save_ret_addr", "save_old_topstk", 
	"set_new_topstk", "save_ret_val", "restore_ret_addr", 
	"restore_old_topstk", "goto_retaddr"
};

void ThreeAddressCode::initTAC() {
	
}

ThreeAddressCode::ThreeAddressCode(attr result, int index, OpCode opCode) {
	initTAC();
	_operation = opCode;
	_result = result;
	_constIndex = index;
}

ThreeAddressCode::ThreeAddressCode(attr result, attr operand1, OpCode opCode) {
	initTAC();
	_operation = opCode;
	_result = result;
	_operand1 = operand1;
}

ThreeAddressCode::ThreeAddressCode(std::string gotoLabel, OpCode opCode) {
	initTAC();
	_operation = opCode;
	_gotoLabel = gotoLabel;
}

ThreeAddressCode::ThreeAddressCode(attr result, attr operand1, attr operand2, OpCode opCode) {
	initTAC();
	_operation = opCode;
	_result = result;
	_operand1 = operand1;
	_operand2 = operand2;
}

ThreeAddressCode::ThreeAddressCode(attr operand, std::string targetLabel, OpCode opCode) {
	initTAC();
	_operation = opCode;
	_operand1 = operand;
	_gotoLabel = targetLabel;
}

ThreeAddressCode::ThreeAddressCode(attr result, std::string label, int paramCount, OpCode opCode) {
	initTAC();
	_operation = opCode;
	_result = result;
	_gotoLabel = label;
	_parameterCount = paramCount;
}

ThreeAddressCode::ThreeAddressCode(attr operand, OpCode opCode) {
	initTAC();
	_operation = opCode;
	if(opCode == TAC_READ) {
		_result = operand;
	} else {
		_operand1 = operand;
	}
} 
/*
ThreeAddressCode::ThreeAddressCode(OpCode opCode, int paramAddr) {
	_operation = opCode;
	if(opCode == TAC_SAVE_RET_ADDR || opCode == TAC_SAVE_OLD_TOPSTK) {
		destAddress = paramAddr;
	} else {
		_operand1.addr = paramAddr;
	}
}
*/
ThreeAddressCode::ThreeAddressCode(OpCode opCode) {
	initTAC();
	_operation = opCode;
}

void ThreeAddressCode::setLabel(std::string pLabel) {
	_label = pLabel;
}

ThreeAddressCode::~ThreeAddressCode()
{
}

bool ThreeAddressCode::hasLabel() const {
	return !_label.empty();
}

OpCode ThreeAddressCode::getOpCode() const {
	return _operation;
}

std::string ThreeAddressCode::getLabel() const {
	return _label;
}

std::string ThreeAddressCode::getGotoLabel() const {
	return _gotoLabel;
}

int ThreeAddressCode::getOperand1() const {
	if(_operation == TAC_NUM_CONST || _operation == TAC_CHAR_CONST) return _constIndex;
	return _operand1.addr;
}

int ThreeAddressCode::getOperand2() const {
	return _operand2.addr;
}

int ThreeAddressCode::getResult() const {
	return _result.addr;
}

int ThreeAddressCode::getConstIndex() const {
	return _constIndex;
}

bool ThreeAddressCode::isOperand1aParam() const {
	return _operand1.param;
}

bool ThreeAddressCode::isOperand2aParam() const {
	return _operand2.param;
}

bool ThreeAddressCode::isResultParam() const {
	return _result.param;
}

std::ostream& ThreeAddressCode::inserter(std::ostream &os) const {
	
	if(!_label.empty()) {
		os << _label << ": ";
	}
	
	if(_operation == TAC_IF_FALSE) {
		os << _opCodeToString[_operation] << " " << _operand1.addr;
	}
	else if(_operation == TAC_GOTO) {
		os << "goto " << _gotoLabel;
	}
	else if(_operation == TAC_ADDRESS_OF) {
		os << _result.addr << " = " << _operand1.addr << "[ " << _operand2.addr << " ]";
	}
	else if(_operation == TAC_ASSIGN_TO_ADDRESS_AT) {
		return os << _result.addr << "[ " << _operand2.addr << " ]" << " = " << _operand1.addr;
		//return os << _operand1.addr << "[ " << _operand2.addr << " ]" << " = " << _result.addr;
	}
	else if(_operation == TAC_NOT) {
		os << _result.addr << " = not " << _operand1.addr;
	}
	else if(_operation == TAC_CALL) {
		os << _result.addr << " = call " << _gotoLabel << " ( " << _parameterCount << " parameters )";
		return os;
	}
	else if(_operation == TAC_PARAM) {
		os << "param " << _operand1.addr;
		return os;
	}
	else if(_operation == TAC_READ) {
		return os << _opCodeToString[_operation] << " " << _result.addr;
	}
	else if(_operation == TAC_WRITE) {
		return os << _opCodeToString[_operation] << " " << _operand1.addr;
	}
	else if(_operation == TAC_NUM_CONST || _operation == TAC_CHAR_CONST) {
		os << _result.addr << " = " << _opCodeToString[_operation] << " #" << _constIndex;
		return os;
	}
	else if(_operation == TAC_SAVE_RET_VAL) {
		return os << _opCodeToString[_operation] << " " << _operand1.addr;
	}
	else if(_operation == TAC_HALT || 
			_operation == TAC_GOTO_RETADDR || 
			_operation == TAC_SET_NEW_TOPSTK || 
			_operation == TAC_NO_OP ) {
		return os << _opCodeToString[_operation];
	}
	else if(_operation >= TAC_SAVE_RET_ADDR) {
		return os << _opCodeToString[_operation];
	}
	else {
		os << _result.addr << " = "  << _operand1.addr << " " << _opCodeToString[_operation];
	}
	
	if(_operand2.addr != -1 && _operation != TAC_ADDRESS_OF) {
		os << " " << _operand2.addr;
	}
	
	if(!_gotoLabel.empty() && _operation != TAC_GOTO) {
		os << " goto " << _gotoLabel;
	}
	
	return os;
}
